/***************************************************************************
 *                                Utility.cs
 *                            -------------------
 *   begin                : May 1, 2002
 *   copyright            : (C) The RunUO Software Team
 *   email                : info@runuo.com
 *
 *   $Id: Utility.cs 234 2007-09-08 07:31:38Z mark $
 *
 ***************************************************************************/

/***************************************************************************
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 ***************************************************************************/

using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Reflection;
using System.Text;
using System.Xml;
using Microsoft.Win32;

namespace WebServer
{
	/// <summary>
	/// Exposes methods for general purpose uses.
	/// </summary>
	public static class GenericUtil
	{
		private static Random m_Random = new Random();
		private static Encoding m_UTF8, m_UTF8WithEncoding;

		/// <summary>
		/// Gets an instance of Encoding.Default
		/// </summary>
		public static Encoding Default
		{
			get
			{
				if (m_UTF8 == null)
					m_UTF8 = new UTF8Encoding(false, false);

				return m_UTF8;
			}
		}

		/// <summary>
		/// Gets an instance of Encoding.Default that should emits the Default identifyer.
		/// </summary>
		public static Encoding UTF8WithEncoding
		{
			get
			{
				if (m_UTF8WithEncoding == null)
					m_UTF8WithEncoding = new UTF8Encoding(true, false);

				return m_UTF8WithEncoding;
			}
		}

		/// <summary>
		/// Rounds the given value to the given decimal places.
		/// </summary>
		/// <param name="value">Value to round.</param>
		/// <param name="digits">Decimal places.</param>
		/// <returns>The rounded value.</returns>
		public static double Round(double value, int digits)
		{
			if ((digits < -15) || (digits > 15))
				return value;

			if (digits >= 0)
				return Math.Round(value, digits);

			double n = Math.Pow(10, -digits);

			return Math.Round(value / n, 0) * n;
		}

		/// <summary>
		/// Rounds the given value to the given decimal places.
		/// </summary>
		/// <param name="d">Value to round.</param>
		/// <param name="decimals">Decimal places.</param>
		/// <returns>The rounded value.</returns>
		public static decimal Round(decimal d, int decimals)
		{
			if ((decimals < -28) || (decimals > 28))
				return d;

			if (decimals >= 0)
				return decimal.Round(d, decimals);

			decimal n = (decimal)Math.Pow(10, -decimals);

			return decimal.Round(d / n, 0) * n;
		}

		/// <summary>
		/// Performs the given operation on the given input with the given incriment.
		/// </summary>
		/// <param name="x">The input to appear on the left-side of the operation.</param>
		/// <param name="op">The character-representation of a standard or bitwise operator.</param>
		/// <param name="incriment">The incriment to appear on the right-side of the operation.</param>
		/// <returns>The incrimented value of the performed operation.</returns>
		public static int Operate(int x, char op, int incriment)
		{
			switch (op)
			{
				default: { return x; }
				case '+': { return x + incriment; }
				case '-': { return x - incriment; }
				case '*': { return x * incriment; }
				case '/': { return x / incriment; }
				case '^': { return x ^ incriment; }
				case '&': { return x & incriment; }
				case '=': { return x = incriment; }
			}
		}

		/// <summary>
		/// Appends the given string value to the given StringBuilder object with the given string seperator value.
		/// </summary>
		/// <param name="sb">StringBuilder object to append to.</param>
		/// <param name="value">The string to be appended.</param>
		/// <param name="separator">The seperator to prepend to the append string value.</param>
		public static void Separate(StringBuilder sb, string value, string separator)
		{
			if (sb.Length > 0)
				sb.Append(separator);

			sb.Append(value);
		}

		/// <summary>
		/// Retrieves the system's reference to the specified string.
		/// </summary>
		/// <param name="str">String object.</param>
		/// <returns>Interned string object reference.</returns>
		public static string Intern(string str)
		{
			if (str == null)
				return null;
			else if (str.Length == 0)
				return String.Empty;

			return String.Intern(str);
		}


		#region To[Something]
		/// <summary>
		/// Converts the specified string representation of a logical value to its boolean equivilent. 
		/// A return value indecates whether the conversion was successful or not.
		/// </summary>
		/// <param name="value">A string containing the value to convert.</param>
		/// <returns>The boolean equivilent.</returns>
		public static bool ToBoolean(string value)
		{
			bool b;
			bool.TryParse(value, out b);

			return b;
		}

		/// <summary>
		/// Converts the specified string representation of a double-precision floating-point number value to its double-precision floating-point number equivilent. 
		/// A return value indecates whether the conversion was successful or not.
		/// </summary>
		/// <param name="value">A string containing the value to convert.</param>
		/// <returns>The double-precision floating-point number equivilent.</returns>
		public static double ToDouble(string value)
		{
			double d;
			double.TryParse(value, out d);

			return d;
		}

		/// <summary>
		/// Converts the specified string representation of a time-span value to its time-span value equivilent. 
		/// A return value indecates whether the conversion was successful or not.
		/// </summary>
		/// <param name="value">A string containing the value to convert.</param>
		/// <returns>The time-span value equivilent.</returns>
		public static TimeSpan ToTimeSpan(string value)
		{
			TimeSpan t;
			TimeSpan.TryParse(value, out t);

			return t;
		}

		/// <summary>
		/// Converts the specified string representation of an Int32 number value to its Int32 number equivilent. 
		/// A return value indecates whether the conversion was successful or not.
		/// </summary>
		/// <param name="value">A string containing the value to convert.</param>
		/// <returns>The Int32 number equivilent.</returns>
		public static int ToInt32(string value)
		{
			int i;

			if (value.StartsWith("0x"))
				int.TryParse(value.Substring(2), NumberStyles.HexNumber, null, out i);
			else
				int.TryParse(value, out i);

			return i;
		}
		#endregion

		#region Get[Something]
		/// <summary>
		/// Converts the specified string representation of an Int32 number value to its Int32 number equivilent. 
		/// XML conversion takes priority.
		/// </summary>
		/// <param name="intString">A string containing the value to convert.</param>
		/// <param name="defaultValue">The default value to return if the conversion fails.</param>
		/// <returns>The Int32 number equivilent on success, the default value on fail.</returns>
		public static int GetInt32(string intString, int defaultValue)
		{
			try
			{
				return XmlConvert.ToInt32(intString);
			}
			catch
			{
				try
				{
					return Convert.ToInt32(intString);
				}
				catch
				{
					return defaultValue;
				}
			}
		}

		/// <summary>
		/// Converts the specified string representation of an time-span value to its time-span value equivilent. 
		/// XML conversion takes priority.
		/// </summary>
		/// <param name="timeSpanString">A string containing the value to convert.</param>
		/// <param name="defaultValue">The default value to return if the conversion fails.</param>
		/// <returns>The time-span value equivilent on success, the default value on fail.</returns>
		public static TimeSpan GetTimeSpan(string timeSpanString, TimeSpan defaultValue)
		{
			try
			{
				return XmlConvert.ToTimeSpan(timeSpanString);
			}
			catch
			{
				return defaultValue;
			}
		}

		/// <summary>
		/// Gets the attribute data for the given XML node and attribute name as a string value.
		/// </summary>
		/// <param name="node">XmlElement node to search.</param>
		/// <param name="attributeName">Attribute name to search for.</param>
		/// <returns>The attribute data for the given XML node and attribute name as a string value.</returns>
		public static string GetAttribute(XmlElement node, string attributeName)
		{
			return GetAttribute(node, attributeName, null);
		}

		/// <summary>
		/// Gets the attribute data for the given XML node and attribute name as a string value.
		/// </summary>
		/// <param name="node">XmlElement node to search.</param>
		/// <param name="attributeName">Attribute name to search for.</param>
		/// <param name="defaultValue">The default value to return if the search is unsuccessful.</param>
		/// <returns>The attribute data for the given XML node and attribute name as a string value.</returns>
		public static string GetAttribute(XmlElement node, string attributeName, string defaultValue)
		{
			if (node == null)
				return defaultValue;

			XmlAttribute attr = node.Attributes[attributeName];

			if (attr == null)
				return defaultValue;

			return attr.Value;
		}

		/// <summary>
		/// Gets the value for the given XML node as a string value.
		/// </summary>
		/// <param name="node">XmlElement node to search.</param>
		/// <param name="defaultValue">The default value to return if the search is unsuccessful.</param>
		/// <returns>The value for the given XML node as a string value.</returns>
		public static string GetText(XmlElement node, string defaultValue)
		{
			if (node == null)
				return defaultValue;

			return node.InnerText;
		}
		#endregion

		/// <summary>
		/// Gets a random double-precision floating-point number value between 0.0 and 1.0
		/// </summary>
		/// <returns>A random double-precision floating-point number value between 0.0 and 1.0</returns>
		public static double RandomDouble()
		{
			return m_Random.NextDouble();
		}

		/// <summary>
		/// Gets the total value of all upwards-facing dice faces after rolling them. 
		/// 4d6+8 is the equivilent to Utility.Dice( 4, 6, 8 )
		/// </summary>
		/// <param name="numDice">Number of dice to roll</param>
		/// <param name="numSides">Number of sides per dice.</param>
		/// <param name="bonus">A bonus value to add to the final roll.</param>
		/// <returns>The total value of all upwards-facing dice faces after rolling them.</returns>
		public static int Dice(int numDice, int numSides, int bonus)
		{
			int total = 0;
			for (int i = 0; i < numDice; ++i)
				total += Random(numSides) + 1;
			total += bonus;
			return total;
		}

		/// <summary>
		/// Gets a random element object from the specified list object.
		/// </summary>
		/// <param name="list">List of objects to retrieve.</param>
		/// <returns>A random element object from the specified list object.</returns>
		public static int RandomList(params int[] list)
		{
			return list[m_Random.Next(list.Length)];
		}

		/// <summary>
		/// Gets a random boolean value, true or false.
		/// </summary>
		/// <returns>A random boolean value, true or false.</returns>
		public static bool RandomBool()
		{
			return (m_Random.Next(2) == 0);
		}

		/// <summary>
		/// Gets a random number between the specified min and max number values, including the min and max values.
		/// </summary>
		/// <param name="min">Minimum value.</param>
		/// <param name="max">Maximum value.</param>
		/// <returns>A random number between the specified min and max number values, including the min and max values.</returns>
		public static int RandomMinMax(int min, int max)
		{
			if (min > max)
			{
				int copy = min;
				min = max;
				max = copy;
			}
			else if (min == max)
			{
				return min;
			}

			return min + m_Random.Next((max - min) + 1);
		}

		/// <summary>
		/// Gets the sum of the base number plus the specified random number.
		/// </summary>
		/// <param name="baseNo">Base number.</param>
		/// <param name="count">The value to be randomised and added/subtracted to/from the base number.</param>
		/// <returns>The sum of the base number plus the specified random number.</returns>
		public static int Random(int baseNo, int count)
		{
			if (count == 0)
			{
				return baseNo;
			}
			else if (count > 0)
			{
				return baseNo + m_Random.Next(count);
			}
			else
			{
				return baseNo - m_Random.Next(-count);
			}
		}

		/// <summary>
		/// Gets a random number based on the specified count.
		/// </summary>
		/// <param name="count">The number to randomise.</param>
		/// <returns>A random number based on the specified count.</returns>
		public static int Random(int count)
		{
			return m_Random.Next(count);
		}
	}
}